﻿#include "precomp.h"
#include "jcurvewidget_p.h"
#include "../jcurve.h"
#include "jsiftareaeditor.h"
#include "qwt_plot_grid.h"
#include "qwt_plot_marker.h"
#include "qwt_picker_machine.h"
#include "qwt_plot_legenditem.h"
#include "qwt_plot_layout.h"
#include "qwt_plot_panner.h"
#include "qwt_plot_magnifier.h"
#include "qwt_plot_canvas.h"
#include "qwt_symbol.h"
#include "qwt_plot_shapeitem.h"
#include "qwt_plot_zoneitem.h"
#include "qwt_plot_renderer.h"
#include "jbaselineshapeitem.h"

// - class JPlotDirectPainter -

JPlotDirectPainter::JPlotDirectPainter(QObject *parent) :
    QwtPlotDirectPainter(parent)
{

}

JPlotDirectPainter::~JPlotDirectPainter()
{

}

bool JPlotDirectPainter::eventFilter(QObject *o, QEvent *e)
{
    Q_UNUSED(o);
    Q_UNUSED(e);
    return false;
}

// - class JTimeScaleDraw -

JTimeScaleDraw::JTimeScaleDraw(Qt::TimeSpec timeSpec) :
    QwtDateScaleDraw(timeSpec),
    q_format("hh:mm:ss:zzz")
{
    //setLabelRotation(-10);
}

void JTimeScaleDraw::setFormat(const QString &format)
{
    q_format = format;
}

QwtText JTimeScaleDraw::label(double v) const
{
    return QwtText(QTime::fromMSecsSinceStartOfDay((int)v).toString(q_format));
}

// - class JCanvasPicker -

JCanvasPicker::JCanvasPicker(QwtPlot *plot) :
    QObject(plot),
    q_selectedCurve(0),
    q_selectedPoint(-1),
    q_isEnabled(false),
    q_mousePressed(false),
    q_mouseMoved(false)
{
    QwtPlotCanvas *canvas = qobject_cast<QwtPlotCanvas *>(plot->canvas());
    if (canvas != 0) {
        canvas->installEventFilter(this);
        canvas->setFocusPolicy(Qt::StrongFocus);
        canvas->setCursor(Qt::CrossCursor);
        canvas->setFocusIndicator(QwtPlotCanvas::ItemFocusIndicator);
        canvas->setFocus();
    }

    //
    shiftPointCursor(true);
}

void JCanvasPicker::clearSelect()
{
    q_selectedCurve = 0;
    q_selectedPoint = -1;
    q_mousePressed = false;
    q_mouseMoved = false;
}

const QwtPlotCurve *JCanvasPicker::selectedCurve() const
{
    return q_selectedCurve;
}

bool JCanvasPicker::isEnabled() const
{
    return q_isEnabled;
}

bool JCanvasPicker::event(QEvent *e)
{
    if (e->type() == QEvent::User) {
        //
        return true;
    }

    return QObject::event(e);
}

bool JCanvasPicker::eventFilter(QObject *o, QEvent *e)
{
    QwtPlot *_plot = plot();
    if (_plot == 0 || o != _plot->canvas()) {
        return false;
    }

    //
    switch (e->type()) {
    case QEvent::FocusIn:
    {
        showCurve(true);
        break;
    }
    case QEvent::FocusOut:
    {
        showCurve(false);
        break;
    }
    case QEvent::Paint:
    {
        QApplication::postEvent(this, new QEvent(QEvent::User));
        break;
    }
    case QEvent::MouseButtonPress:
    {
        q_mousePressed = true;
        const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(e);
        if (select(mouseEvent->pos())) {
            _plot->setCursor(Qt::PointingHandCursor);
            return true;
        }
        break;
    }
    case QEvent::MouseButtonRelease:
    {
        q_mousePressed = false;
        q_mouseMoved = false;
        _plot->unsetCursor();
        break;
    }
    case QEvent::MouseMove:
    {
        q_mouseMoved = true;
        break;
    }
    case QEvent::KeyPress:
    {
        const QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
        switch (keyEvent->key()) {
        case Qt::Key_Up:
        {
            shiftCurveCursor(true);
            return true;
        }
        case Qt::Key_Down:
        {
            shiftCurveCursor(false);
            return true;
        }
        case Qt::Key_Right:
        case Qt::Key_Plus:
        {
            if (q_selectedCurve != 0) {
                shiftPointCursor(true);
            } else {
                shiftCurveCursor(true);
            }
            return true;
        }
        case Qt::Key_Left:
        case Qt::Key_Minus:
        {
            if (q_selectedCurve != 0) {
                shiftPointCursor(false);
            } else {
                shiftCurveCursor(false);
            }
            return true;
        }
        default:
            break;
        }
        break;
    }
    default:
        break;
    }

    return false;
}

void JCanvasPicker::setEnabled(bool on)
{
    if (on == q_isEnabled) {
        return;
    }

    QwtPlot *plot = this->plot();
    if (plot != 0) {
        q_isEnabled = on;
        if (on) {
            plot->canvas()->installEventFilter(this);
        } else {
            plot->canvas()->removeEventFilter(this);
        }
    }
}

bool JCanvasPicker::select(const QPoint &pos)
{
    QwtPlot *_plot = plot();
    if (_plot == 0) {
        return false;
    }

    //
    int selectedIndex = -1;
    JCurve *selectedCurve = 0;

    const QwtPlotItemList &items = _plot->itemList(JCurve::Rtti_Curve);
    for (QwtPlotItemIterator citer = items.begin();
         citer != items.end(); ++citer) {
        if ((*citer)->rtti() == JCurve::Rtti_Curve) {
            JCurve *curve = static_cast<JCurve *>(*citer);
            if (curve == 0 || !curve->isVisible()) {
                continue;
            }

            qreal d = 0;
            selectedIndex = curve->closestPoint(pos, &d);
            if (selectedIndex != -1 && d < 10) {   // 10 pixels tolerance
                selectedCurve = curve;
                break;
            }
        }
    }

    //
    showCurve(false);
    q_selectedCurve = 0;
    q_selectedPoint = -1;

    //
    if (selectedCurve != 0) {
        q_selectedPoint = selectedIndex;
        q_selectedCurve = selectedCurve;
        showCurve(true);
        Q_EMIT selectedChanged(selectedIndex, selectedCurve);  //
        return true;
    }

    return false;
}

void JCanvasPicker::release()
{
    Q_ASSERT(false);    // no implement
}

void JCanvasPicker::showCurve(bool showIt)
{
    if (q_selectedCurve == 0) {
        return;
    }

    QwtSymbol *symbol = const_cast<QwtSymbol *>(q_selectedCurve->symbol());
    const QBrush brush = symbol->brush();  // save brush
    if (showIt) {
        symbol->setBrush(symbol->brush().color().dark(100));
    }

    JPlotDirectPainter directPainter;
    directPainter.drawSeries(q_selectedCurve, q_selectedPoint, q_selectedPoint);

    if (showIt) {
        symbol->setBrush(brush);            // restore brush
    }
}

void JCanvasPicker::shiftPointCursor(bool up)
{
    if (q_selectedCurve == 0) {
        return;
    }

    int index = q_selectedPoint + (up ? 1 : -1);
    index = (index + q_selectedCurve->dataSize()) % q_selectedCurve->dataSize();
    if (index != q_selectedPoint) {
        showCurve(false);
        q_selectedPoint = index;
        showCurve(true);
    }
}

void JCanvasPicker::shiftCurveCursor(bool up)
{
    QwtPlot *_plot = plot();
    if (_plot == 0) {
        return;
    }

    //
    const QwtPlotItemList &items = _plot->itemList();
    QwtPlotItemIterator citer = items.begin();
    QwtPlotItemList curveList;
    for (; citer != items.end(); ++citer) {
        if ((*citer)->rtti() == JCurve::Rtti_Curve) {
            curveList.append(*citer);
        }
    }

    //
    if (curveList.isEmpty()) {
        return;
    }

    citer = curveList.begin();
    if (q_selectedCurve != 0) {
        for (; citer != curveList.end(); ++citer) {
            if (q_selectedCurve == *citer) {
                break;
            }
        }

        //
        if (citer == curveList.end()) {     // not found
            citer = curveList.begin();
        }

        if (up) {
            ++citer;
            if (citer == curveList.end()) {
                citer = curveList.begin();
            }
        } else {
            if (citer == curveList.begin()) {
                citer = curveList.end();
            }

            --citer;
        }
    }

    showCurve(false);
    q_selectedPoint = 0;
    q_selectedCurve = static_cast<QwtPlotCurve *>(*citer);
    showCurve(true);
}

QwtPlot *JCanvasPicker::plot()
{
    return qobject_cast<QwtPlot *>(parent());
}

const QwtPlot *JCanvasPicker::plot() const
{
    return qobject_cast<const QwtPlot *>(parent());
}

// - class JTimePlotPicker -

JTimePlotPicker::JTimePlotPicker(QWidget *canvas)
    : QwtPlotPicker(canvas)
    , d_isTimeXAxis(true)
{

}

JTimePlotPicker::JTimePlotPicker(int xAxis, int yAxis, QWidget *canvas)
    : QwtPlotPicker(xAxis, yAxis, canvas)
{

}

JTimePlotPicker::JTimePlotPicker(int xAxis, int yAxis,
                                 QwtPicker::RubberBand rubberBand,
                                 QwtPicker::DisplayMode trackerMode, QWidget *canvas)
    : QwtPlotPicker(xAxis, yAxis, rubberBand, trackerMode, canvas)
{

}

bool JTimePlotPicker::isTimeXAxis() const
{
    return d_isTimeXAxis;
}

void JTimePlotPicker::setTimeXAxis(bool value)
{
    if (value != d_isTimeXAxis) {
        d_isTimeXAxis = value;
        plot()->update();
    }
}

QwtText JTimePlotPicker::trackerTextF(const QPointF &point) const
{
    switch (rubberBand())
    {
    case PolygonRubberBand:
    case CrossRubberBand:
        if (d_isTimeXAxis) {
            return QString("(%1, %2)")
                    .arg(QTime::fromMSecsSinceStartOfDay((int)point.x()).toString("hh:mm:ss:zzz"))
                    .arg(point.y());
        } else {
            return QString("(%1, %2)")
                    .arg(point.x())
                    .arg(point.y());
        }
        break;
    default:
        break;
    }

    return QwtPlotPicker::trackerTextF(point);
}

// - class JLegendItem -

JLegendItem::JLegendItem()
{
    setRenderHint(QwtPlotItem::RenderAntialiased);
    setAlignment(Qt::AlignTop | Qt::AlignRight);

    setTextPen(QColor(0xdb, 0xdb, 0xdb, 200));
    setBorderPen(QColor(150, 150, 150, 150));
    setBackgroundBrush(QColor(150, 150, 150, 20));
    setBorderRadius(3);
    setMargin(2);
    setSpacing(0);
    setItemMargin(0);
    setMaxColumns(1);

    QFont _font = font();
    _font.setPointSize(9);
    setFont(_font);
}

// - class JPlotZoomer -

JPlotZoomer::JPlotZoomer(int xAxis, int yAxis, QWidget *canvas):
    QwtPlotZoomer(xAxis, yAxis, canvas),
    d_currentShapeIndex(-1),
    d_zoomModeEnabled(false),
    d_siftModeEnabled(false),
    d_isTimeXAxis(true)
{
    // RightButton: zoom out by 1
    // Ctrl+RightButton: zoom out to full size
    setMousePattern(QwtEventPattern::MouseSelect2,
                    Qt::RightButton, Qt::ControlModifier);
    setMousePattern(QwtEventPattern::MouseSelect6,
                    Qt::RightButton, Qt::ShiftModifier);

    setRubberBand(QwtPicker::RectRubberBand);
    setRubberBandPen(QColor(Qt::green));
    setTrackerMode(QwtPicker::AlwaysOn);
    setTrackerPen(QColor(0xdb, 0xdb, 0xdb));
    //setResizeMode(QwtPicker::KeepSize);

    //
    setZoomMode(false);
    setSiftMode(true);
}

QwtText JPlotZoomer::trackerTextF(const QPointF &point) const
{
    switch (rubberBand())
    {
    case RectRubberBand:
        if (d_isTimeXAxis) {
            return QString("(%1, %2)")
                    .arg(QTime::fromMSecsSinceStartOfDay((int)point.x()).toString("hh:mm:ss:zzz"))
                    .arg(point.y());
        } else {
            return QString("(%1, %2)")
                    .arg(point.x())
                    .arg(point.y());
        }
        break;
    default:
        break;
    }

    return QwtPlotZoomer::trackerTextF(point);
}

JSiftAreaEditor *JPlotZoomer::areaSift() const
{
    QwtPlot *_plot =  const_cast<QwtPlot *>(plot());
    if (_plot == 0) {
        return false;
    }

    //
    JCurveWidget *curveWidget = qobject_cast<JCurveWidget *>(_plot->parent());
    if (curveWidget == 0) {
        return false;
    }

    //
    return curveWidget->siftAreaEditor();
}

bool JPlotZoomer::isTimeXAxis() const
{
    return d_isTimeXAxis;
}

void JPlotZoomer::setTimeXAxis(bool value)
{
    if (value != d_isTimeXAxis) {
        d_isTimeXAxis = value;
        plot()->update();
    }
}

void JPlotZoomer::setZoomMode(bool enabled)
{
    if (enabled != d_zoomModeEnabled) {
        d_zoomModeEnabled = enabled;
        //
        setEnabled(d_zoomModeEnabled || d_siftModeEnabled);
    }
}

void JPlotZoomer::setSiftMode(bool enabled)
{
    if (enabled != d_siftModeEnabled) {
        d_siftModeEnabled = enabled;
        //
        setEnabled(d_zoomModeEnabled || d_siftModeEnabled);
    }
}

bool JPlotZoomer::accept(QPolygon &polygon) const
{
    //
    if (d_zoomModeEnabled) {
        return QwtPlotZoomer::accept(polygon);
    }

    //
    if (d_siftModeEnabled) {
        //
        JSiftAreaEditor *areaSift = this->areaSift();
        if (areaSift == 0) {
            return false;
        }

        QRectF rect = invTransform(polygon.boundingRect());
        if (rect.isValid()) {
            QPainterPath path;
            path.addRect(rect);
            areaSift->setShape(d_currentShapeIndex, path, true);
        }
    }

    return false;
}

void JPlotZoomer::begin()
{
    QwtPlotZoomer::begin();

    //
    if (d_siftModeEnabled) {
        //
        JSiftAreaEditor *areaSift = this->areaSift();
        if (areaSift == 0) {
            return;
        }
        //
        d_currentShapeIndex = areaSift->shapeCount() - 1;
    }
}

bool JPlotZoomer::end(bool ok)
{
    //
    if (d_siftModeEnabled) {
        //
        JSiftAreaEditor *areaSift = this->areaSift();
        if (areaSift == 0) {
            return false;
        }
    }

    return QwtPlotZoomer::end(ok);
}

// - class JCavansOptionWidget -

JCavansOptionWidget::JCavansOptionWidget(QwtPlot *plot, QWidget *parent) :
    QWidget(parent),
    d_plot(plot),
    q_editEnabled(false)
{
    setObjectName("JCavansOptionWidget");
    setFixedHeight(30);

    QHBoxLayout *horiLayoutMain = new QHBoxLayout(this);
    horiLayoutMain->setContentsMargins(20, 2, 20, 2);
    horiLayoutMain->setSpacing(2);

    d_checkBoxZoomMode = new QCheckBox(QStringLiteral("缩放模式"), this);
    horiLayoutMain->addWidget(d_checkBoxZoomMode);

    d_checkBoxSiftMode = new QCheckBox(QStringLiteral("筛选模式"), this);
    horiLayoutMain->addSpacing(20);
    horiLayoutMain->addWidget(d_checkBoxSiftMode);

    horiLayoutMain->addStretch();

    d_checkBoxAnti = new QCheckBox(QStringLiteral("抗锯齿"), this);
    horiLayoutMain->addWidget(d_checkBoxAnti);

    horiLayoutMain->addSpacing(20);

    q_comboBoxCurveStyle = new QComboBox(this);
    q_comboBoxCurveStyle->addItem(QStringLiteral("点"));
    q_comboBoxCurveStyle->addItem(QStringLiteral("折线"));
    q_comboBoxCurveStyle->addItem(QStringLiteral("拟合曲线"));
    horiLayoutMain->addWidget(q_comboBoxCurveStyle);

    horiLayoutMain->addStretch();

    //
    q_buttonEdit = new QPushButton(QStringLiteral("数据截取"), this);
    horiLayoutMain->addWidget(q_buttonEdit);
    q_buttonEdit->setMinimumWidth(80);

    horiLayoutMain->addStretch();

    d_checkBoxBaseLine = new QCheckBox(QStringLiteral("显示母线"), this);
    horiLayoutMain->addWidget(d_checkBoxBaseLine);

    horiLayoutMain->addSpacing(20);

    //
    QPushButton *buttonReset = new QPushButton(QStringLiteral("重置"), this);
    q_buttonExport = new QPushButton(QStringLiteral("导出"), this);
    buttonReset->setMinimumWidth(80);
    q_buttonExport->setMinimumWidth(80);
    horiLayoutMain->addWidget(buttonReset);
    horiLayoutMain->addSpacing(20);
    horiLayoutMain->addWidget(q_buttonExport);

    //
    connect(d_checkBoxZoomMode, &QCheckBox::toggled, [=](bool checked){
        Q_EMIT zoomModeChanged(checked);
        if (checked) {
            d_checkBoxSiftMode->setChecked(false);
        }
    });
    connect(d_checkBoxSiftMode, &QCheckBox::toggled, [=](bool checked){
        Q_EMIT siftModeChanged(checked);
        if (checked) {
            d_checkBoxZoomMode->setChecked(false);
        }
    });
    connect(d_checkBoxAnti, &QCheckBox::toggled, [=](bool checked){
        Q_EMIT antiChanged(checked);
    });
    connect(d_checkBoxBaseLine, &QCheckBox::toggled, [=](bool checked){
        Q_EMIT baseLineEnabledChanged(checked);
    });
    connect(q_comboBoxCurveStyle, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
            [=](int index){
        int curveStyle = JCurveWidget::CurveStyle_Invalid;
        switch (index) {
        case 0:
            curveStyle = JCurveWidget::CurveStyle_Dots;
            break;
        case 1:
            curveStyle = JCurveWidget::CurveStyle_Lines;
            break;
        case 2:
            curveStyle = JCurveWidget::CurveStyle_Fitted;
            break;
        default:
            break;
        }
        Q_EMIT curveStyleChanged(curveStyle);
    });
    connect(q_buttonEdit, &QPushButton::clicked, [=](){
        Q_EMIT editClicked();
    });
    connect(buttonReset, &QPushButton::clicked, [=](){
        //
        d_plot->setAxisAutoScale(QwtPlot::xBottom);
        d_plot->setAxisAutoScale(QwtPlot::yLeft);
        //
        Q_EMIT this->reset();
    });
    connect(q_buttonExport, &QPushButton::clicked, [=](){
        //
        QString title = d_plot->title().text();
        if (title.isEmpty()) {
            title = "snapshot1";
        } else {
            title.remove("</font>").remove("<font color='#aaf'>");
        }
        QwtPlotRenderer renderer;
        renderer.exportTo(d_plot, title.simplified().append(QStringLiteral(".pdf")));
    });

    //
    q_buttonEdit->setVisible(q_editEnabled);
}

QwtPlot *JCavansOptionWidget::plot()
{
    return d_plot;
}

const QwtPlot *JCavansOptionWidget::plot() const
{
    return d_plot;
}

bool JCavansOptionWidget::isZoomMode() const
{
    return d_checkBoxZoomMode->isChecked();
}

bool JCavansOptionWidget::isSiftMode() const
{
    return d_checkBoxSiftMode->isChecked();
}

bool JCavansOptionWidget::isBaseLineVisible() const
{
    return d_checkBoxBaseLine->isVisible();
}

bool JCavansOptionWidget::isBaseLineEnabled() const
{
    return d_checkBoxBaseLine->isChecked();
}

bool JCavansOptionWidget::isCurveStyleVisible() const
{
    return q_comboBoxCurveStyle->isVisible();
}

bool JCavansOptionWidget::isEditEnabled() const
{
    return q_editEnabled;
}

bool JCavansOptionWidget::isExportVisible() const
{
    return q_buttonExport->isVisible();
}

JCurveWidget::CurveStyle JCavansOptionWidget::curveStyle() const
{
    return (JCurveWidget::CurveStyle)q_comboBoxCurveStyle->currentIndex();
}

void JCavansOptionWidget::setCurveStyle(JCurveWidget::CurveStyle curveStyle)
{
    switch (curveStyle) {
    case JCurveWidget::CurveStyle_Dots:
        q_comboBoxCurveStyle->setCurrentIndex(0);
        break;
    case JCurveWidget::CurveStyle_Lines:
        q_comboBoxCurveStyle->setCurrentIndex(1);
        break;
    case JCurveWidget::CurveStyle_Fitted:
        q_comboBoxCurveStyle->setCurrentIndex(2);
        break;
    default:
        break;
    }
}

void JCavansOptionWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);

    painter.save();
    painter.setPen(QPen(QColor(100, 100, 100, 150)));
    painter.drawRect(rect());
    painter.restore();
}

void JCavansOptionWidget::setZoomMode(bool checked)
{
    d_checkBoxZoomMode->setChecked(checked);
}

void JCavansOptionWidget::setSiftMode(bool checked)
{
    d_checkBoxSiftMode->setChecked(checked);
}

void JCavansOptionWidget::setZoomModeVisible(bool visible)
{
    d_checkBoxZoomMode->setVisible(visible);
}

void JCavansOptionWidget::setSiftModeVisible(bool visible)
{
    d_checkBoxSiftMode->setVisible(visible);
}

void JCavansOptionWidget::setBaseLineVisible(bool visible)
{
    d_checkBoxBaseLine->setVisible(visible);
}

void JCavansOptionWidget::setBaseLineEnabled(bool enabled)
{
    d_checkBoxBaseLine->setChecked(enabled);
}

void JCavansOptionWidget::setCurveStyleVisible(bool visible)
{
    q_comboBoxCurveStyle->setVisible(visible);
}

void JCavansOptionWidget::setEditEnabled(bool enabled)
{
    if (enabled != q_editEnabled) {
        q_editEnabled = enabled;
        Q_EMIT editEnabledChanged(enabled);
        q_buttonEdit->setVisible(enabled);
    }
}

void JCavansOptionWidget::setExportVisible(bool visible)
{
    q_buttonExport->setVisible(visible);
}

// - class JDatabaseEdit -

JDatabaseEdit::JDatabaseEdit(QWidget *parent) :
    QDialog(parent)
{
    setWindowTitle(QStringLiteral("数据截取编辑"));
    setWindowFlags(windowFlags() | Qt::WindowMinMaxButtonsHint);

    QVBoxLayout *vertLayoutMain = new QVBoxLayout(this);

    QGroupBox *groupBoxSettings = new QGroupBox(QStringLiteral("设置"), this);
    vertLayoutMain->addWidget(groupBoxSettings);

    QFormLayout *formLayoutSettings = new QFormLayout(groupBoxSettings);
    formLayoutSettings->setAlignment(Qt::AlignRight);

    q_timeEditMin = new QTimeEdit(this);
    q_timeEditMax = new QTimeEdit(this);
    q_checkBoxRetain = new QCheckBox(QStringLiteral("保留时间范围内数据"), this);
    q_timeEditMin->setDisplayFormat("hh:mm:ss:zzz");
    q_timeEditMax->setDisplayFormat("hh:mm:ss:zzz");
    q_checkBoxRetain->setChecked(true);

    formLayoutSettings->addRow(QStringLiteral("时间下限："), q_timeEditMin);
    formLayoutSettings->addRow(QStringLiteral("时间上限："), q_timeEditMax);
    formLayoutSettings->addRow(QStringLiteral("保留数据："), q_checkBoxRetain);

    q_buttonApply = new QPushButton(QStringLiteral("保存到数据库"), this);
    q_buttonApply->setMinimumWidth(120);
    vertLayoutMain->addWidget(q_buttonApply, 0, Qt::AlignRight);

    //
    connect(q_buttonApply, SIGNAL(clicked(bool)), SIGNAL(apply()));

    //
    setMinimumWidth(400);
}

QTime JDatabaseEdit::minimumTime() const
{
    return q_timeEditMin->time();
}

void JDatabaseEdit::setMinimumTime(const QTime &time)
{
    if (time <= q_timeEditMax->time()) {
        q_timeEditMin->setTime(time);
    } else {
        q_timeEditMin->setTime(q_timeEditMax->time());
    }
}

QTime JDatabaseEdit::maximumTime() const
{
    return q_timeEditMax->time();
}

void JDatabaseEdit::setMaximumTime(const QTime &time)
{
    if (time > q_timeEditMin->time()) {
        q_timeEditMax->setTime(time);
    } else {
        q_timeEditMax->setTime(q_timeEditMin->time());
    }
}

bool JDatabaseEdit::isRetain() const
{
    return q_checkBoxRetain->isChecked();
}

void JDatabaseEdit::setRetain(bool retain)
{
    q_checkBoxRetain->setChecked(retain);
}

void JDatabaseEdit::setRange(const QTime &minimum, const QTime &maximum)
{
    if (minimum > maximum) {
        return;
    }

    q_timeEditMin->setTime(minimum);
    q_timeEditMax->setTime(maximum);
}

// - class JCurveWidgetPrivate -

void JCurveWidgetPrivate::init(bool zoom, const QString &title)
{
    Q_Q(JCurveWidget);

    q->setObjectName("JCurveWidget");

    QVBoxLayout *vertLayoutMain = new QVBoxLayout(q);
    vertLayoutMain->setContentsMargins(0, 0, 0, 0);
    vertLayoutMain->setSpacing(1);

    plot  = new QwtPlot(title, q);
    plot->setMouseTracking(false);
    optionWidget = new JCavansOptionWidget(plot, q);

    //
    databaseEdit = new JDatabaseEdit(q);
    databaseEdit->hide();

    vertLayoutMain->addWidget(optionWidget);
    vertLayoutMain->addWidget(plot);

    plot->setAutoDelete(true);
    plot->setContentsMargins(1, 1, 1, 1);
    plot->plotLayout()->setAlignCanvasToScales(true);
    plot->setAutoReplot();

    QwtPlotGrid *grid = new QwtPlotGrid();
    grid->setPen(QColor(80, 80, 80, 150), 0.5, Qt::SolidLine);
    grid->enableX(true);
    grid->enableY(true);
    grid->attach(plot);

    marker = new QwtPlotMarker();
    marker->setLineStyle(QwtPlotMarker::Cross);
    marker->setLinePen(QColor(100, 100, 255, 150), 1.0, Qt::DashLine);
    marker->attach(plot);

    //
    siftAreaEditor = new JSiftAreaEditor(plot);
    siftAreaEditor->setEnabled(true);

    //
    picker = new JTimePlotPicker(QwtPlot::xBottom, QwtPlot::yLeft,
                                 QwtPlotPicker::CrossRubberBand, QwtPicker::ActiveOnly,
                                 plot->canvas());
    picker->setStateMachine(new QwtPickerTrackerMachine());
    picker->setTrackerMode(QwtPicker::ActiveOnly);
    picker->setRubberBandPen(QColor(150, 150, 100, 200));
    picker->setTrackerPen(QColor(200, 200, 100, 200));

    // zoomer
    if (zoom) {
        //
        zoomer = new JPlotZoomer(QwtPlot::xBottom, QwtPlot::yLeft, plot->canvas());
        zoomer->setTrackerMode(QwtPicker::AlwaysOff);

        //
        QObject::connect(optionWidget, SIGNAL(zoomModeChanged(bool)),
                         zoomer, SLOT(setZoomMode(bool)));
        QObject::connect(optionWidget, SIGNAL(siftModeChanged(bool)),
                         zoomer, SLOT(setSiftMode(bool)));
    }

    // panning with the left mouse button
    QwtPlotPanner *panner = new QwtPlotPanner(plot->canvas());
    panner->setMouseButton(Qt::MidButton);

    // zoom in/out with the wheel
    QwtPlotMagnifier *magnifier = new QwtPlotMagnifier(plot->canvas());
    magnifier->setMouseButton(Qt::RightButton);

    // Legend
    legend = new JLegendItem;
    legend->setMaxColumns(1);
    legend->attach(plot);

    //
    //QwtScaleWidget *rightAxis = plot->axisWidget(QwtPlot::yRight);
    //plot->enableAxis(QwtPlot::yRight);

    // canvas picker
    canvasPicker = new JCanvasPicker(plot);

    // baseCurve
    baseCurve = new JBaseLineShapeItem();
    baseCurve->setVisible(optionWidget->isBaseLineEnabled());
    baseCurve->attach(plot);

    //
    QObject::connect(siftAreaEditor, SIGNAL(siftAreaChanged(int,QRectF)),
                     q, SIGNAL(siftAreaChanged(int,QRectF)));
    QObject::connect(canvasPicker, SIGNAL(selectedChanged(int,JCurve*)),
                     q, SIGNAL(selectedChanged(int,JCurve*)));
    QObject::connect(canvasPicker, &JCanvasPicker::selectedChanged,
                     [=](int selectedIndex, JCurve *curve){
        QPointF pos = curve->sample(selectedIndex);
        QPainterPath path;
        path.addEllipse(pos, 1e-6, 1e-6);
        siftAreaEditor->setShape(0, path, true);
    });
    QObject::connect(optionWidget, SIGNAL(siftModeChanged(bool)),
                     siftAreaEditor, SLOT(setEnabled(bool)));
    QObject::connect(optionWidget, SIGNAL(siftModeChanged(bool)),
                     canvasPicker, SLOT(setEnabled(bool)));
    QObject::connect(optionWidget, &JCavansOptionWidget::zoomModeChanged, [=](bool checked){
        if (optionWidget->isSiftMode()) {
            magnifier->setEnabled(true);
        } else {
            magnifier->setEnabled(checked);
        }
    });
    QObject::connect(optionWidget, &JCavansOptionWidget::reset, [=](){
        zoomer->setZoomBase();
    });
    QObject::connect(optionWidget, &JCavansOptionWidget::baseLineEnabledChanged, [=](bool enabled){
        baseCurve->setVisible(enabled);
        Q_EMIT q->baseLineEnabledChanged(enabled);
    });
    QObject::connect(optionWidget, &JCavansOptionWidget::antiChanged, [=](bool enabled){
        QVectorIterator<JCurve *> citer(curves);
        while (citer.hasNext()) {
            JCurve *curve = citer.next();
            curve->setRenderHint(QwtPlotItem::RenderAntialiased, enabled);
        }
    });
    QObject::connect(optionWidget, &JCavansOptionWidget::curveStyleChanged, [=](int curveStyle){
        QVectorIterator<JCurve *> citer(curves);
        while (citer.hasNext()) {
            JCurve *curve = citer.next();
            curve->setCurveStyle(curveStyle);
        }
    });
    //
    QObject::connect(optionWidget, SIGNAL(editClicked()), q, SIGNAL(editClicked()));
    //
    QObject::connect(optionWidget, &JCavansOptionWidget::editClicked, [=](){
        //
        if (databaseEdit->isHidden()) {
            //
            q->clearHotSpot();
        }
        q->setSiftMode(true);
        //
        double minimum = plot->axisScaleDiv(QwtPlot::xBottom).lowerBound();
        double maximum = plot->axisScaleDiv(QwtPlot::xBottom).upperBound();
        databaseEdit->setMinimumTime(QTime::fromMSecsSinceStartOfDay((int)minimum));
        databaseEdit->setMaximumTime(QTime::fromMSecsSinceStartOfDay((int)maximum));
        databaseEdit->setRetain(true);
        //
        databaseEdit->show();
        databaseEdit->activateWindow();
        databaseEdit->setFocus();
    });
    //
    QObject::connect(databaseEdit, &JDatabaseEdit::finished, [=](){
        //
        q->clearHotSpot();
    });
    //
    QObject::connect(databaseEdit, &JDatabaseEdit::apply, [=](){
        //
        q->clearHotSpot();
        //
        Q_EMIT q->timeBandApply(databaseEdit->minimumTime(),
                                databaseEdit->maximumTime(),
                                databaseEdit->isRetain());
    });
    //
    QObject::connect(siftAreaEditor, &JSiftAreaEditor::siftAreaChanged,
                     [=](int, const QRectF &range){
        //
        if (optionWidget->isEditEnabled() && databaseEdit->isVisible()) {
            databaseEdit->setMinimumTime(QTime::fromMSecsSinceStartOfDay((int)range.left()));
            databaseEdit->setMaximumTime(QTime::fromMSecsSinceStartOfDay((int)range.right()));
        }
    });

    //
    optionWidget->setZoomMode(false);
    optionWidget->setSiftMode(true);

    //
    q->setAxisXYType(JCurveWidget::AxisXY_Time_Value);

    //
    optionWidget->setCurveStyleVisible(true);
    optionWidget->setCurveStyle(JCurveWidget::CurveStyle_Dots);
}
